package com.example.boot.redis;


import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.data.redis.serializer.GenericToStringSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * RedisManager 操作类
 * 使用SpringDataRedis进行整合
 *
 * @author yy
 * @since 1.0.0, 2022年08月20日
 */
@Component
public class RedisManager {

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    /*
     * 一、Key 键操作
     */

    /**
     * 实现命令：TTL key，以秒为单位，返回给定 key的剩余生存时间(TTL, time to live)。
     *
     * @param key 要操作的Key
     * @return 剩余生存秒数
     */
    public long ttl(String key) {
        return stringRedisTemplate.getExpire(key);
    }

    /**
     * 实现命令：expire 设置过期时间，单位:秒
     *
     * @param key     要操作的Key
     * @param timeout 过期时间，单位：秒
     * @return 操作是否成功
     */
    public Boolean expire(String key, long timeout) {
        return stringRedisTemplate.expire(key, timeout, TimeUnit.SECONDS);
    }

    /**
     * 实现命令：INCR key
     * 将 key 中储存的数字值增一个值delta
     *
     * @param key   key值
     * @param delta 对应增加的量
     * @return 执行完成后Value的值
     */
    public long incr(String key, long delta) {
        return stringRedisTemplate.opsForValue().increment(key, delta);
    }

    /**
     * 实现命令：KEYS pattern
     * 查找所有符合给定模式 pattern的 key
     *
     * @param pattern 去匹配的模式
     * @return 匹配的结果
     */
    public Set<String> keys(String pattern) {
        return stringRedisTemplate.keys(pattern);
    }

    /**
     * 实现命令：DEL key，删除一个key
     *
     * @param key 要删除的KEY
     * @return 是否操作成功
     */
    public Boolean del(String key) {
        return stringRedisTemplate.delete(key);
    }

    /*
     * 二、String（字符串）操作
     */

    /**
     * 实现命令：SET key value，设置一个key-value（将字符串值 value关联到 key）
     *
     * @param key   对应要保存的key
     * @param value 要保存的value值
     */
    public void set(String key, String value) {
        stringRedisTemplate.opsForValue().set(key, value);
    }

    /**
     * 实现命令：SET key value EX seconds
     * 设置key-value和超时时间（秒）
     *
     * @param key     对应要保存的key
     * @param value   对应的value值
     * @param timeout 超时时间（以秒为单位）
     */
    public void set(String key, String value, long timeout) {
        stringRedisTemplate.opsForValue().set(key, value, timeout, TimeUnit.SECONDS);
    }

    /**
     * 实现命令：GET key，返回 key所关联的字符串值。
     *
     * @param key 对应需要查询的key
     * @return value key对应的value值
     */
    public String get(String key) {
        return stringRedisTemplate.opsForValue().get(key);
    }

    /*
     * 三、Hash（哈希表）操作
     */

    /**
     * 实现命令：HSET key field value，
     * 将哈希表 key中的域 field的值设为 value
     *
     * @param key   对应要设置的key
     * @param field 对应hash中的字段名称
     * @param value 要设置的值
     */
    public void hset(String key, String field, Object value) {
        stringRedisTemplate.opsForHash().put(key, field, value);
    }
    public void hmset(String key, Map<Object, Object> value ) {
        stringRedisTemplate.opsForHash().putAll(key,  value);
    }
    public Set<Object> hkeys(String key) {
      return   stringRedisTemplate.opsForHash().keys(key);
    }

    /**
     * 实现命令：HGET key field，返回哈希表 key中给定域 field的值
     *
     * @param key   对应要的key值
     * @param field 对应hash中的字段名称
     * @return 获取的结果
     */
    public String hget(String key, String field) {
        return (String) stringRedisTemplate.opsForHash().get(key, field);
    }

    /**
     * 实现命令：HDEL key field [field ...]，删除哈希表 key 中的一个或多个指定域，不存在的域将被忽略。
     *
     * @param key    对应要的key值
     * @param fields 对应要删除的字段数据-可变长参数列表
     * @return 成功删除了几个属性
     */
    public Long hdel(String key, Object... fields) {
        return stringRedisTemplate.opsForHash().delete(key, fields);
    }

    /**
     * 实现命令：HGETALL key，返回哈希表 key中，所有的域和值。
     *
     * @param key 对应要获取的hash的key值
     * @return key对应的查询结果，hash整体
     */
    public Map<Object, Object> hgetall(String key) {
        return stringRedisTemplate.opsForHash().entries(key);
    }

    /*
     * 四、List（列表）
     */

    /**
     * 实现命令：LPUSH key value
     * 将一个值 value插入到列表 key的表头
     *
     * @param key   对应要加的节点Key值
     * @param value 要添加的value
     * @return 执行 LPUSH命令后，列表的长度。
     */
    public long lpush(String key, String value) {
        return stringRedisTemplate.opsForList().leftPush(key, value);
    }

    /**
     * 实现命令：LPOP key，
     * 移除并返回列表 key的头元素。
     *
     * @param key 要移除的对象
     * @return 列表key的头元素。
     */
    public String lpop(String key) {
        return stringRedisTemplate.opsForList().leftPop(key);
    }

    /**
     * 实现命令：RPUSH key value，
     * 将一个值 value插入到列表 key的表尾(最右边)。
     *
     * @param key   要插入的key值
     * @param value 要插入的value值
     * @return 执行 LPUSH命令后，列表的长度。
     */
    public long rpush(String key, String value) {
        return stringRedisTemplate.opsForList().rightPush(key, value);
    }

    public void zadd(String key, String value,  double score) {
        stringRedisTemplate.opsForZSet().add(key,value, score);
    }
    public Set<String> zrange(String key,long start, long end) {
         return stringRedisTemplate.opsForZSet().range(key, start,  end);
    }


    public Set<ZSetOperations.TypedTuple<String> > rangeWithScores(String key, long start, long end) {
        return stringRedisTemplate.opsForZSet().rangeWithScores(key, start,  end);
    }



    public Object lua(RedisScript redisScript, List<String> keys, Long defaultExpire) {

        RedisSerializer<String> stringRedisSerializer = stringRedisTemplate.getStringSerializer();
        Object result = stringRedisTemplate.execute(redisScript, stringRedisSerializer, stringRedisSerializer, keys, String.valueOf(defaultExpire));

        System.out.println(result);
        return result;
    }

    /**
     * 执行 lua 脚本
     * @author hengyumo
     * @since 2021-06-05
     *
     * @param luaScript  lua 脚本
     * @param returnType 返回的结构类型
     * @param keys       KEYS
     * @param argv       ARGV
     * @param <T>        泛型
     *
     * @return 执行的结果
     */
    public <T> Object executeLuaScript(String luaScript, Class<T> returnType, String[] keys, String... argv) {
        return  stringRedisTemplate.execute(RedisScript.of(luaScript, returnType),
                new StringRedisSerializer(),
                new GenericToStringSerializer<>(returnType),
                Arrays.asList(keys),
                (Object[])argv);
    }

    /**
     * @author hengyumo
     * @since 2021-06-05
     *
     * 删除以key为前缀的所有键值
     * @param keyPre 前缀
     * @return  返回删除掉的所有key
     */
    public List<String> deleteKeysWithPre(String luaScript,String keyPre) {
        @SuppressWarnings("unchecked")
        List<Object> result = (List<Object>) executeLuaScript(luaScript, List.class, new String[] {keyPre});
        return result.stream().map(x -> {
            if (x instanceof List) {
                @SuppressWarnings("unchecked")
                List<String> list = (List<String>) x;
                if (list.size() > 0) {
                    return list.get(0);
                }
            }
            return null;
        }).filter(Objects::nonNull).collect(Collectors.toList());
    }

    /**
     * 执行lua 脚本
     * @param defaultRedisScript
     * @param keyList key 值集合
     * @return
     */
    public Object execute(DefaultRedisScript<String> defaultRedisScript, List<String> keyList) {
        return stringRedisTemplate.execute(defaultRedisScript, keyList);
    }

    /**
     * 执行lua 脚本
     * @param longDefaultRedisScript
     * @param asList  key 值集合
     * @param argv   vlue 的判断条件
     * @param <T>
     * @return
     */
    public <T> Object executeLong(DefaultRedisScript<Long> longDefaultRedisScript, List<String> asList,   Object[] argv) {
        return   stringRedisTemplate.execute(longDefaultRedisScript, asList, argv);
    }
    /**
     * 执行lua 脚本
     * @param longDefaultRedisScript
     * @param asList  key 值集合
     * @param argv   vlue 的判断条件
     * @param <T>
     * @return
     */
    public <T> Object execute(DefaultRedisScript<String> longDefaultRedisScript, List<String> asList,   Object[] argv) {
        return   stringRedisTemplate.execute(longDefaultRedisScript, asList, argv);
    }

    /**
     * 执行lua 脚本
     * @param longDefaultRedisScript
     * @param asList  key 值集合
     * @param value1  vlue 的第一个参数判断条件
     * @param value2  vlue 的第二个参数判断条件
     * @param <T>
     * @return
     */
    public <T> Object execute(DefaultRedisScript<Long> longDefaultRedisScript, List<String> asList, String value1, String value2) {
      return   stringRedisTemplate.execute(longDefaultRedisScript, asList, value1,value2);
    }
}